﻿using System;
using System.Collections.Specialized;
using System.Linq;
using System.Collections.Generic;
using System.Web.Mvc;
using ControleRepresentante.Common;
using ControleRepresentante.Logic.Models;
using ControleRepresentante.Logic.Repositories;
using ControleRepresentante.Logic.Statistics;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using WebSite.Controllers;
using WebSite.ViewModels.DetailsViewModels;
using WebSite.ViewModels.EditViewModels;

namespace ControleRepresentante.UnitTests.Controllers
{
    [TestClass]
    public class UsuarioControllerTests : CRUDControllerTestScenarioBase<UsuarioController, User, IUserRepository, StatisticsBase>
    {
        private const string Username = "username";
        private const string Password = "password";
        private const string Email = "email";
        private const bool Ativo = true;

        readonly Dictionary<Area, IEnumerable<Operacao>> m_Permissoes = new Dictionary<Area, IEnumerable<Operacao>>
        {
            {Area.Atributos, new[] {Operacao.View, Operacao.Create}}                                                           
        };

        private FormCollection CreateFormCollection()
        {
            NameValueCollection nameValueCollection = new NameValueCollection();
            foreach (KeyValuePair<Area, IEnumerable<Operacao>> permissao in m_Permissoes)
            {
                foreach (Operacao operacao in permissao.Value)
                {
                    nameValueCollection.Add(permissao.Key + "_" + operacao, true.ToString());
                }
            }
            return new FormCollection(nameValueCollection);
        }

        protected override bool ShouldTestIndexEmpty
        {
            get { return false; }
        }

        protected override UsuarioController CreateController(bool isRepresentante = true)
        {
            this.LoginAsAdmin();
            Guid representanteId = this.CreateRepresentante("repre", true, "repreuser");
            RepositoryProvider.GetRepository<IRepresentanteRepository>().GetModelById(representanteId);
            this.CreateRepresentante("otherrepre", false, "other");
            if (isRepresentante)
            {
                this.Logout();
                this.LoginAs("repreuser");
            }
            return new UsuarioController(RepositoryProvider, AreaOperacaoRegistry, SessionConfiguration, ControllerExtensionProvider);
        }

        protected override User CreateModel()
        {
            User user = new User {Username = Guid.NewGuid().ToString()};
            user.SetPasswordFromRawText(Guid.NewGuid().ToString());
            return user;
        }

        protected override ActionResult Create(UsuarioController controller)
        {
            return controller.Create(Username, Password, Email, Ativo, null, null, null, null, CreateFormCollection());
        }

        protected override void VerifyModel(User model)
        {
            Assert.AreEqual(Username, model.Username);
            Assert.IsFalse(model.IsAdmin);
            Assert.AreEqual(Ativo, model.Ativo);
            Assert.AreEqual(Email, model.Email);
            Assert.AreEqual(Password.Encrypt(), model.Password);
            VerifyPermissoes(model);
            Representante representante = GetCurrentRepresentante();
            Assert.AreEqual(1, representante.Users.Count(x => x.Id == model.Id));
        }

        private void VerifyPermissoes(User user)
        {
            foreach (Permissao permissao in user.Permissoes)
            {
                IEnumerable<Operacao> operacoes;
                if (m_Permissoes.TryGetValue(permissao.Area, out operacoes))
                {
                    Assert.AreEqual(operacoes.Aggregate(Operacao.None, (a, b) => a | b), permissao.Operacao);
                }
                else
                {
                    Assert.AreEqual(Operacao.None, permissao.Operacao);
                }
            }
        }

        protected override ActionResult CreateEmptyFields(UsuarioController controller)
        {
            return controller.Create(String.Empty, String.Empty, String.Empty, Ativo, null, null, null, null, CreateFormCollection());
        }

        protected override void VerifyModelEmptyFields(User model)
        {
            Assert.AreEqual(String.Empty, model.Username);
            Assert.IsFalse(model.IsAdmin);
            Assert.AreEqual(Ativo, model.Ativo);
            Assert.AreEqual(String.Empty, model.Email);
            Assert.IsNull(model.Password);
            VerifyPermissoes(model);
        }

        protected override ActionResult CreateDuplicatedModel(UsuarioController controller, User model)
        {
            return controller.Create(model.Username, Password, Email, Ativo, null, null, null, null, CreateFormCollection());
        }

        protected override void VerifyDuplicatedModel(User originalModel, User duplicatedModel)
        {
            Assert.AreEqual(originalModel.Username, duplicatedModel.Username);
            Assert.IsFalse(duplicatedModel.IsAdmin);
            Assert.AreEqual(Ativo, duplicatedModel.Ativo);
            Assert.AreEqual(Email, duplicatedModel.Email);
            Assert.AreEqual(Password.Encrypt(), duplicatedModel.Password);
            VerifyPermissoes(duplicatedModel);
        }

        protected override ActionResult Edit(UsuarioController controller)
        {
            return controller.Edit(Username, Password, Email, Ativo, null, null, null, null, CreateFormCollection());
        }

        protected override ActionResult EditEmptyFields(UsuarioController controller)
        {
            return controller.Edit(String.Empty, String.Empty, String.Empty, Ativo, null, null, null, null, CreateFormCollection());
        }

        protected override ActionResult EditDuplicatedModel(UsuarioController controller, User modelToDuplicate)
        {
            return controller.Edit(modelToDuplicate.Username, Password, Email, Ativo, null, null, null, null, CreateFormCollection());
        }

        [TestMethod]
        public void TestDeleteUserWithPost()
        {
            this.LoginAsAdmin();
            const string UserToDelete = "usertodelete";
            Guid representanteId = this.CreateRepresentante("repre", true, Username, UserToDelete);
            IRepresentanteRepository representanteRepository = RepositoryProvider.GetRepository<IRepresentanteRepository>();
            Representante representante = representanteRepository.GetModelById(representanteId);
            Guid userId = representante.Users.Single(x => x.Username == UserToDelete).Id;
            this.Logout();
            this.LoginAs(UserToDelete);
            Guid vendedorId = this.CreateVendedor(new Vendedor{Nome = "vendedor"});
            Cliente cliente = new Cliente
            {
                RazaoSocial = "cliente",
                NomeFantasia = "cliente",
                Vendedor = new Vendedor {Id = vendedorId}
            };
            Guid clienteId = this.CreateCliente(cliente);
            Guid transportadoraId = this.CreateTransportadora(new Transportadora{Nome = "transportadora"});
            Guid fabricaId = this.CreateFabrica("fabrica");
            Pedido pedido = new Pedido
            {
                Cliente = new Cliente {Id = clienteId},
                Vendedor = new Vendedor {Id = vendedorId},
                Fabrica = new Fabrica {Id = fabricaId},
                Transportadora = new Transportadora {Id = transportadoraId}
            };
            pedido.Id = this.CreatePedido(pedido);
            this.AddPost(pedido.Id, "comentario");
            this.Logout();
            this.LoginAs(Username);
            this.DeleteUser(userId);

            IPedidoRepository pedidoRepository = RepositoryProvider.GetRepository<IPedidoRepository>(ImpostProvider);
            Assert.AreEqual(0, pedidoRepository.GetModelById(pedido.Id).Posts.Count());

            this.Logout();
            this.LoginAsAdmin();

            representante = representanteRepository.GetModelById(representanteId);
            Assert.AreEqual(1, representante.Users.Count());
        }

        [TestMethod]
        public void TestUnableToDeleteConfirmCurrentUser()
        {
            this.LoginAsAdmin();
            Guid representanteId = this.CreateRepresentante("repre", true, "u");
            IRepresentanteRepository representanteRepository = RepositoryProvider.GetRepository<IRepresentanteRepository>();
            Representante representante = representanteRepository.GetModelById(representanteId);
            Guid userId = representante.Users.Single().Id;
            this.Logout();
            this.LoginAs("u");
            using (UsuarioController usuarioController = new UsuarioController(RepositoryProvider, AreaOperacaoRegistry, SessionConfiguration, ControllerExtensionProvider))
            {
                ActionResult actionResult = usuarioController.Delete(userId);
                Assert.IsInstanceOfType(actionResult, typeof(ViewResult));
                ViewResult viewResult = (ViewResult) actionResult;
                Assert.IsInstanceOfType(viewResult.Model, typeof(UsuarioDetailsViewModel));
                UsuarioDetailsViewModel usuarioDetailsViewModel = (UsuarioDetailsViewModel) viewResult.Model;
                Assert.IsNotNull(usuarioDetailsViewModel.Message);
            }
        }

        [TestMethod]
        public void TestUnableToDeleteCurrentUser()
        {
            this.LoginAsAdmin();
            Guid representanteId = this.CreateRepresentante("repre", true, "u");
            IRepresentanteRepository representanteRepository = RepositoryProvider.GetRepository<IRepresentanteRepository>();
            Representante representante = representanteRepository.GetModelById(representanteId);
            Guid userId = representante.Users.Single().Id;
            this.Logout();
            this.LoginAs("u");
            using (UsuarioController usuarioController = new UsuarioController(RepositoryProvider, AreaOperacaoRegistry, SessionConfiguration, ControllerExtensionProvider))
            {
                ActionResult actionResult = usuarioController.Delete(userId, null);
                Assert.IsInstanceOfType(actionResult, typeof(ViewResult));
                ViewResult viewResult = (ViewResult) actionResult;
                Assert.IsInstanceOfType(viewResult.Model, typeof(UsuarioDetailsViewModel));
                UsuarioDetailsViewModel usuarioDetailsViewModel = (UsuarioDetailsViewModel) viewResult.Model;
                Assert.IsNotNull(usuarioDetailsViewModel.Message);
            }
        }

        [TestMethod]
        public void TestUnableToEditPageCurrentUser()
        {
            this.LoginAsAdmin();
            Guid representanteId = this.CreateRepresentante("repre", true, "u");
            IRepresentanteRepository representanteRepository = RepositoryProvider.GetRepository<IRepresentanteRepository>();
            Representante representante = representanteRepository.GetModelById(representanteId);
            Guid userId = representante.Users.Single().Id;
            this.Logout();
            this.LoginAs("u");
            using (UsuarioController usuarioController = new UsuarioController(RepositoryProvider, AreaOperacaoRegistry, SessionConfiguration, ControllerExtensionProvider))
            {
                ActionResult actionResult = usuarioController.Edit(userId);
                Assert.IsInstanceOfType(actionResult, typeof(ViewResult));
                ViewResult viewResult = (ViewResult)actionResult;
                Assert.IsInstanceOfType(viewResult.Model, typeof(UsuarioEditViewModel));
                UsuarioEditViewModel usuarioEditViewModel = (UsuarioEditViewModel)viewResult.Model;
                Assert.IsNotNull(usuarioEditViewModel.Message);
            }
        }
    }
}